﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml.Serialization;

using ABCat.Core;
using ABCat.Core.Plugins;
using ABCat.Core.Plugins.DataProviders;
using ABCat.Core.Plugins.DataSets;
using ABCat.Core.Plugins.Sites;
using ABCat.Shared;
using ABCat.Shared.Properties;

namespace ABCat.Plugins.Parsers.Rutracker
{
	[PluginCreator("Rutracker TorrentFiles Downloader", WorkModeEnum.PerContext)]
	public class RutrackerTorrentFilesDownloader : RecordTargetDownloaderBase
	{
		#region + Fields +

		private DateTime _lastLoadFromInternet;

		#endregion
		#region + Properties +

		protected override string LoginUrl
		{
			get { return "http://login.rutracker.org/forum/login.php"; }
		}

		#endregion
		#region + Ctor +

		public RutrackerTorrentFilesDownloader(Context context)
			: base(context)
		{
		}

		#endregion
		#region + Logic +


		public override void DownloadRecordTarget(string loginCoockies, IAudioBook record, IDbContainer dbContainer, Action<int, int, string> progressCallback, CancellationToken cancellationToken)
		{
			var config = Config.Load<RutrackerTorrentFilesDownloaderConfig>(Context);

			while ((DateTime.Now - _lastLoadFromInternet).TotalSeconds < 10)
			{
				Thread.Sleep(100);
				if (cancellationToken.IsCancellationRequested) return;
			}

			if (cancellationToken.IsCancellationRequested) return;

			var torrentFileData = DownloadData(GetTorrentFileUrl(record), loginCoockies + GetCurrentTorrentCookieKey(record.Key));
			_lastLoadFromInternet = DateTime.Now;

			if (!CheckTorrentFileForIncorrectLogin(torrentFileData)) throw new ABCatException("Не удалось загрузить файл torrent. Проверьте логин/пароль пользователя rutracker", ExceptionTypeEnum.Info, null, true);

			string torrentFileName = Path.Combine(config.TorrentFilesFolder, "{0}_{1}_{2}.torrent".F(record.GroupKey, record.Key, DateTime.Now.Ticks));
			Directory.CreateDirectory(config.TorrentFilesFolder);
			File.WriteAllBytes(torrentFileName, torrentFileData);
			//StartUTorrent(tempFilePath, targetLibraryPath);

			var userData = dbContainer.UserDataSet.CreateUserData();
			userData.RecordGroupKey = record.GroupKey;
			userData.RecordKey = record.Key;

			switch (config.TorrentFileAction)
			{
				case RutrackerTorrentFilesDownloaderConfig.TorrentFileActionEnum.None:
					Process.Start("explorer.exe", "/select, \"{0}\"".F(torrentFileName));
					break;
				case RutrackerTorrentFilesDownloaderConfig.TorrentFileActionEnum.SystemAction:
					Process.Start(torrentFileName);
					break;
				case RutrackerTorrentFilesDownloaderConfig.TorrentFileActionEnum.StartDownload:
					string targetLibraryPath = GetAbsoluteLibraryPath(record);
					userData.LocalPath = targetLibraryPath;
					string commandLineArguments = "/directory \"{0}\" \"{1}\"".F(targetLibraryPath, torrentFileName);
					var ia = config.GetTorrentClient();
					if (ia != null && !ia.ExePath.IsNullOrEmpty() && File.Exists(ia.ExePath)) Process.Start(ia.ExePath, commandLineArguments);
					else
					{
						Process.Start("explorer.exe", "/select, \"{0}\"".F(torrentFileName));
					}
					break;
			}

			dbContainer.UserDataSet.AddUserData(userData);
			dbContainer.SaveChanges();
		}

		private bool CheckTorrentFileForIncorrectLogin(byte[] downloadedData)
		{
			using (var ms = new MemoryStream(downloadedData))
			{
				var br = new BinaryReader(ms, Encoding.Default);
				var arrayForCheck = new byte[8];
				br.Read(arrayForCheck, 0, 8);

				string possibleIncorrectLogin = Encoding.Default.GetString(arrayForCheck);
				return possibleIncorrectLogin != "<center>";
			}
		}

		public override string GetAbsoluteLibraryPath(IAudioBook record)
		{
			var config = Config.Load<RutrackerTorrentFilesDownloaderConfig>(Context);
			string bookPath = GetRecordTargetLibraryPath(record);
			return Path.Combine(config.AudioCatalogFolder, bookPath);

		}

		public override string GetRecordPageUrl(IAudioBook record)
		{
			return @"http://rutracker.org/forum/viewtopic.php?t={0}".F(record.Key);
		}

		public override string GetRecordTargetLibraryPath(IAudioBook record)
		{
			string result;
			if (record.Author != null)
			{
				result = GetFileName(record.Author);
			}
			else
			{
				result = GetFileName(record.Title);
			}
			return result;
		}

		/// <summary>
		/// Пришлось перегрузить этот метод, поскольку рутрекер высылает куки
		/// для авторизации одновременно с 302ым редиректом. WebClient
		/// автоматически переходит на новый урл и теряет нужные нам куки.
		/// </summary>
		/// <returns></returns>
		protected override string GetLoginCookies([CanBeNull] LoginInfo loginInfo)
		{
			var config = Config.Load<RutrackerTorrentFilesDownloaderConfig>(Context);

			if (loginInfo == null && config.SaveCoockies && File.Exists(config.CoockieFileName))
			{
				LoginCookies = File.ReadAllText(config.CoockieFileName);
			}
			else if (loginInfo != null)
			{
				var sb = new StringBuilder();
				var loginData = GetLoginData(loginInfo);

				if (loginData != null)
				{
					for (int i = 0; i < loginData.Count; i++)
					{
						sb.Append(string.Format("{0}={1}", loginData.AllKeys[i], loginData[i]));
						if (i + 1 < loginData.Count) sb.Append('&');
					}
				}
				var dataBytes = Encoding.UTF8.GetBytes(sb.ToString());

				var request = (HttpWebRequest)WebRequest.Create(LoginUrl);
				request.Method = "POST";
				request.ContentType = "application/x-www-form-urlencoded";
				request.ContentLength = dataBytes.Length;

				var requestStream = request.GetRequestStream();
				requestStream.Write(dataBytes, 0, dataBytes.Length);
				requestStream.Close();

				//ОЧЕНЬ ВАЖНО
				request.AllowAutoRedirect = false;

				var response = request.GetResponse();
				LoginCookies = RemoveSetCookiesAttributes(response.Headers["Set-Cookie"]); 
			}

			if (!LoginCookies.IsNullOrEmpty() && LoginCookies.Length == 1) LoginCookies = null;

			if (!LoginCookies.IsNullOrEmpty())
			{
				var mainPage = DownloadData("http://rutracker.org/forum/index.php", LoginCookies);
				string pageText = Encoding.Default.GetString(mainPage);
				if (pageText.Contains("<form method=\"post\" action=\"http://login.rutracker.org/forum/login.php\">")) LoginCookies = null;
				else
				{
					if (config.SaveCoockies) File.WriteAllText(config.CoockieFileName, LoginCookies);
				}
			}

			return LoginCookies;
		}

		protected override NameValueCollection GetLoginData(LoginInfo loginInfo)
		{
			return new NameValueCollection() { { "login_username", loginInfo.Login }, { "login_password", loginInfo.GetPassword() }, { "login", "Вход" } };
		}

		protected string GetTorrentFileUrl(IAudioBook record)
		{
			return @"http://dl.rutracker.org/forum/dl.php?t={0}".F(record.Key);
		}

		/// <summary>
		/// На каждой кнопке 'Скачать' висит событие,
		/// которое прописывает в кукисы специальный ключ,
		/// без которого Торрент не скачать. Вылавливае мего
		/// </summary>
		/// <param name="rutrackerId"></param>
		/// <returns></returns>
		private string GetCurrentTorrentCookieKey(string rutrackerId)
		{
			return string.Format("bb_dl={0}", rutrackerId);
		}

		private string GetFileName(string rawFileName)
		{
			var sb = new StringBuilder();

			var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars());

			foreach (char c in rawFileName)
			{
				if (invalidChars.Contains(c)) sb.Append("_");
				else sb.Append(c);
			}

			return sb.ToString();
		}
		private RutrackerTorrentFilesDownloaderConfig _config;

		public override bool CheckForConfig(bool correct, out Config incorrectConfig)
		{
			_config = Config.Load<RutrackerTorrentFilesDownloaderConfig>(Context);
			bool checkResult = _config.Check(correct);
			incorrectConfig = checkResult ? null : _config;
			return checkResult;
		}

		#endregion
	}
}